package leetcode.tester;

import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;

public class Tester {

	/**
	 * 函数式接口，允许接收可变数量的 Object参数并返回 Object结果。
	 * 使用 Object... 表示支持任意数量和类型的参数
	 * `@FunctionalInterface` 注解确保它只有一个抽象方法
	 */
	@FunctionalInterface
	public interface VarArgsFunction {
		Object apply(Object... args) throws Exception;
	}

	public static void test(VarArgsFunction function, Object[][] inputs, Object[] outputs) {
		test(function, inputs, outputs, null);
	}

	// 核心测试方法
	public static void test(VarArgsFunction function, Object[][] inputs, Object[] outputs, ArgsEnum[] args) {
		boolean ignoreOrder = false;
		if (args != null) {
			List<ArgsEnum> list = Arrays.asList(args);
			if (list.contains(ArgsEnum.IGNORE_ORDER)) {
				ignoreOrder = true;
			}
		}
		List<Double> timeList = new ArrayList<>();
		for (int i = 0; i < inputs.length; i++) {
			Object[] input = inputs[i].clone();  //进行深度拷贝避免修改原始输入
			Object expected = outputs[i];
			try {
				long start = System.nanoTime();
				Object actual = function.apply(input);
				long end = System.nanoTime();

				boolean isCorrect = deepEquals(expected, actual, ignoreOrder);

				double runTime = (end - start) / 1_000_000.0;
				System.out.println("测试用例 " + (i + 1) + ": " + (isCorrect ? "✅ 正确" : "❌ 错误") + " | 执行时间: " + runTime + " ms");
				timeList.add(runTime);

				if (!isCorrect) {
					System.out.println("   输入: " + Arrays.deepToString(input));
					System.out.println("   期望输出: " + objToString(expected));
					System.out.println("   实际输出: " + objToString(actual));
				}
			} catch (Exception e) {
				System.out.println("测试用例 " + (i + 1) + " 发生异常: " + e.getMessage());
				e.printStackTrace();
			}
		}
		double sum = timeList.stream().mapToDouble(Double::doubleValue).sum();
		System.out.println("总计用时: " + sum + " ms");
	}

	// ----------- 适配器，方便用方法引用 -----------------

	// 接收一个参数的适配器
	public static <T, R> VarArgsFunction wrap(Function<T, R> func) {
		return (Object... args) -> func.apply((T) args[0]);
	}

	// 接收两个参数的适配器
	public static <T, U, R> VarArgsFunction wrap(BiFunction<T, U, R> func) {
		return (Object... args) -> func.apply((T) args[0], (U) args[1]);
	}

	// 以后可以继续扩展 TriFunction, QuadFunction ...

	// ----------- 辅助方法 -----------------
	private static boolean deepEquals(Object o1, Object o2, boolean ignoreOrder) {
		if (o1 == null || o2 == null) return o1 == o2;

		// 数组
		if (o1.getClass().isArray() && o2.getClass().isArray()) {
			return compareArrays(o1, o2, ignoreOrder);
		}

		// List
		if (o1 instanceof List && o2 instanceof List) {
			return compareLists((List<?>) o1, (List<?>) o2, ignoreOrder);
		}

		// Set
		if (o1 instanceof Set && o2 instanceof Set) {
			return compareSets((Set<?>) o1, (Set<?>) o2);
		}

		// Map
		if (o1 instanceof Map && o2 instanceof Map) {
			return compareMaps((Map<?, ?>) o1, (Map<?, ?>) o2, ignoreOrder);
		}

		// 其他对象
		if (o1.equals(o2)) return true;

		return objToString(o1).equals(objToString(o2));
	}

	/**
	 * 比较数组（含基本类型、多维、可忽略顺序）
	 */
	private static boolean compareArrays(Object arr1, Object arr2, boolean ignoreOrder) {
		int len1 = java.lang.reflect.Array.getLength(arr1);
		int len2 = java.lang.reflect.Array.getLength(arr2);
		if (len1 != len2) return false;

		// 转为 Object[] 统一处理
		Object[] o1 = toObjectArray(arr1);
		Object[] o2 = toObjectArray(arr2);

		if (!ignoreOrder) {
			for (int i = 0; i < len1; i++) {
				if (!deepEquals(o1[i], o2[i], false)) return false;
			}
			return true;
		}

		// ignoreOrder = true，用频次计数方式比较
		java.util.Map<Object, Integer> countMap = new java.util.HashMap<>();
		for (Object item : o1) {
			countMap.merge(objKey(item), 1, Integer::sum);
		}
		for (Object item : o2) {
			Object key = objKey(item);
			if (!countMap.containsKey(key)) return false;
			countMap.put(key, countMap.get(key) - 1);
			if (countMap.get(key) == 0) countMap.remove(key);
		}
		return countMap.isEmpty();
	}


	/**
	 * 比较两个List是否相同
	 */
	private static boolean compareLists(List<?> l1, List<?> l2, boolean ignoreOrder) {
		if (l1.size() != l2.size()) return false;

		if (!ignoreOrder) {
			for (int i = 0; i < l1.size(); i++) {
				if (!deepEquals(l1.get(i), l2.get(i), false)) return false;
			}
			return true;
		}

		// ignoreOrder = true，用频次计数
		java.util.Map<Object, Integer> countMap = new java.util.HashMap<>();
		for (Object item : l1) {
			countMap.merge(objKey(item), 1, Integer::sum);
		}
		for (Object item : l2) {
			Object key = objKey(item);
			if (!countMap.containsKey(key)) return false;
			countMap.put(key, countMap.get(key) - 1);
			if (countMap.get(key) == 0) countMap.remove(key);
		}
		return countMap.isEmpty();
	}


	/**
	 * 比较两个Set是否相同
	 */
	private static boolean compareSets(Set<?> s1, Set<?> s2) {
		if (s1.size() != s2.size()) return false;
		// 转成 key 进行统一比较
		java.util.Set<Object> set1 = s1.stream().map(Tester::objKey).collect(java.util.stream.Collectors.toSet());
		java.util.Set<Object> set2 = s2.stream().map(Tester::objKey).collect(java.util.stream.Collectors.toSet());
		return set1.equals(set2);
	}

	/**
	 * 比较两个Map是否相同
	 */
	private static boolean compareMaps(Map<?, ?> m1, Map<?, ?> m2, boolean ignoreOrder) {
		if (m1.size() != m2.size()) return false;
		for (Object key1 : m1.keySet()) {
			Object matchKey = findMatchingKey(m2, key1);
			if (matchKey == null) return false;
			if (!deepEquals(m1.get(key1), m2.get(matchKey), ignoreOrder)) return false;
		}
		return true;
	}

	/**
	 *
	 */
	private static Object findMatchingKey(Map<?, ?> map, Object key) {
		for (Object k : map.keySet()) {
			if (deepEquals(k, key, false)) return k;
		}
		return null;
	}


	/**
	 * 判断是否是数字
	 */
	private static boolean isNumber(Object o) {
		return o instanceof Number;
	}

	/**
	 * 判断类型、生成唯一 key
	 */
	private static Object objKey(Object o) {
		if (isNumber(o)) return new java.math.BigDecimal(o.toString());
		if (o instanceof CharSequence) return o.toString();
		if (o instanceof Boolean) return o;
		return o.toString(); // 最后退化为 toString
	}

	/**
	 * 数组转为 Object[]
	 */
	private static Object[] toObjectArray(Object array) {
		int length = java.lang.reflect.Array.getLength(array);
		Object[] result = new Object[length];
		for (int i = 0; i < length; i++) {
			result[i] = java.lang.reflect.Array.get(array, i);
		}
		return result;
	}


	private static String objToString(Object obj) {
		if (obj == null) return "null";
		if (obj.getClass().isArray()) {
			return Arrays.deepToString(toObjectArray(obj));
		}
		return obj.toString();
	}
}
